//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// <string>

// Make sure basic_string::insert does not cause infinite recursion.
// This is a regression test for a bug that had been introduced in D98573.

#include <algorithm>
#include <cassert>
#include <string>

#include "test_macros.h"
#include "asan_testing.h"

struct char_ascii {
  char char_;

  char_ascii() = default;
  char_ascii(char ch) : char_(ch) { assert(ch <= 0x7f); }
};

template <>
struct std::char_traits<char_ascii> {
  using char_type  = char_ascii;
  using int_type   = typename std::char_traits<char>::int_type;
  using off_type   = typename std::char_traits<char>::off_type;
  using pos_type   = typename std::char_traits<char>::pos_type;
  using state_type = typename std::char_traits<char>::state_type;

  static void assign(char_type& r, char_type const& a) TEST_NOEXCEPT { r = a; }

  static char_type* assign(char_type* p, std::size_t count, char_type a) {
    std::fill(p, p + count, a);
    return p;
  }

  static bool eq(char_type a, char_type b) TEST_NOEXCEPT { return a.char_ == b.char_; }

  static bool lt(char_type a, char_type b) TEST_NOEXCEPT { return a.char_ < b.char_; }

  static std::size_t length(char_type const* s) {
    std::size_t n = 0;
    if (s) {
      while (s->char_)
        ++n;
    }
    return n;
  }

  static const char_type* find(char_type const* p, std::size_t count, char_type const& ch) {
    while (count > 0) {
      if (p->char_ == ch.char_) {
        return p;
      } else {
        ++p;
        --count;
      }
    }
    return nullptr;
  }

  static int compare(char_type const* s1, char_type const* s2, std::size_t count) {
    for (std::size_t i = 0; i < count; ++i) {
      if (s1->char_ < s2->char_)
        return -1;
      else if (s2->char_ < s1->char_)
        return 1;
    }
    return 0;
  }

  static char_type* move(char_type* dest, char_type const* src, std::size_t count) {
    if (src <= dest && dest < src + count) {
      std::copy_backward(src, src + count, dest + count);
    } else {
      std::copy(src, src + count, dest);
    }
    return dest;
  }

  static char_type* copy(char_type* dest, char_type const* src, std::size_t count) {
    return char_traits::move(dest, src, count);
  }
};

int main(int, char**) {
  std::basic_string<char_ascii> str;

  char_ascii ch('A');
  str.insert(str.begin(), &ch, &ch + 1);
  assert(str.size() == 1);
  assert(str[0].char_ == 'A');
  LIBCPP_ASSERT(is_string_asan_correct(str));
  return 0;
}
